Concurrent software systems establish primitives to allow concurrently executing tasks (threads or processes) to share common resources. Such primitives are often referred to as “latches”.
It is known to use an exclusive latching mechanism to permit or deny access to a resource associated with a latch. If a first task holds a latch to a resource, which permits only exclusive access, then a second task requiring the resource must wait until the holding task releases the latch before being able to access the latch.
More complex latches allow both shared and exclusive access to the resource. If a first task holds the latch to a resource in exclusive mode, then a second task requiring the resource must wait until the holding task releases the latch. Alternatively, if a first task holds the latch in share mode, then a second task will be granted access to the resource at the same time, if it requests the latch in share mode as well. If the second task requests the latch in exclusive mode, then it must wait until all tasks holding the latch in share mode release the latch.
Once a latch-holding task has used the latched resource, the resource may be in an inconsistent or unstable state which makes the resource unusable by other tasks. Therefore, before the holding task relinquishes the latch the holding task must carry out steps to return the resource to a consistent or stable state. This may be accomplished by the holding task executing a subroutine that will “clean up” the resource. The resource may then be used by other tasks.
From the description above, it can be seen that there is an overhead cost in executing computer code containing concurrency primitives such as latches. This overhead comprises several hidden costs, including the cost of executing instructions required for the latch-holding task to give up the latch, the cost of informing other tasks requesting the resource of the change of the latch's state when the latch is released, and the cost incurred in making the resource protected by the latch consistent so that it may be accessed by other tasks (the cost of executing the “clean-up subroutine”).
In many systems, the same task repeatedly requests, and releases, the same latch before another task requests the latch. If the system overhead costs referred to above are high, the latching and unlatching steps will result in a decreased efficiency of the system resulting in the overall throughput of the system being reduced.
Certain prior art systems attempt to avoid the above inefficiency by permitting latch-holding task to continue to hold the latch rather than relinquishing it once the task is finished using the resource. In such a system, the latch-holding task must periodically poll the latch to see if there are waiting tasks which have requested it. However, this approach may starve a waiting task because although the resource is not being used, until the latch-holding task polls the latch, the latch is not made available. Further, whenever the latch-holding task polls the latch, CPU time is used.
It is therefore desirable to have a means of efficiently managing concurrency primitives such as latches with reduced system overhead by releasing latches only when necessary, while avoiding the need for polling the latch. Such efficient management would prevent starvation of waiting tasks while saving CPU time.